/*
 * FaceCat图形通讯框架
 * 著作权编号:2015SR229355+2020SR0266727
 * 上海卷卷猫信息技术有限公司
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace FaceCat {
    /// <summary>
    /// 连接状态
    /// </summary>
    public enum ConnectStatus
    {
        SUCCESS,
        CONNECT_PROXY_FAIL,
        NOT_CONNECT_PROXY,
        CONNECT_SERVER_FAIL
    }

    /// <summary>
    /// 客户端套接字连接
    /// </summary>
    public class FCClientSocket {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="proxyType">代理类型</param>
        /// <param name="ip">IP地址</param>
        /// <param name="port">端口</param>
        /// <param name="proxyIp">代理IP</param>
        /// <param name="proxyPort">代理端口</param>
        /// <param name="proxyUserName">用户名</param>
        /// <param name="proxyUserPwd">密码</param>
        /// <param name="proxyDomain">域</param>
        /// <param name="timeout">超时</param>
        public FCClientSocket(long proxyType, String ip, int port, String proxyIp, int proxyPort, String proxyUserName, String proxyUserPwd, String proxyDomain, int timeout, byte[] token) {
            m_blnProxyServerOk = true;
            m_proxyDomain = proxyDomain;
            m_proxyType = proxyType;
            m_ip = ip;
            m_port = port;
            m_proxyIp = proxyIp;
            m_proxyPort = proxyPort;
            m_proxyUserName = proxyUserName;
            m_proxyUserPwd = proxyUserPwd;
            m_timeout = timeout;
            m_token[0] = token[0];
            m_token[1] = token[1];
            m_token[2] = token[2];
            m_token[3] = token[3];
        }

        /// <summary>
        /// 代理服务是否OK
        /// </summary>
        private bool m_blnProxyServerOk;
        /// <summary>
        /// 是否连接
        /// </summary>
        private bool m_connected;
        /// <summary>
        /// 套接字ID
        /// </summary>
        public int m_hSocket;
        /// <summary>
        /// IP地址
        /// </summary>
        private String m_ip;
        /// <summary>
        /// 是否删除
        /// </summary>
        private bool m_isDeleted;
        /// <summary>
        /// 端口
        /// </summary>
        private int m_port;
        /// <summary>
        /// 域
        /// </summary>
        private String m_proxyDomain;
        /// <summary>
        /// 代理类型
        /// </summary>
        private long m_proxyType;
        /// <summary>
        /// 代理IP
        /// </summary>
        private String m_proxyIp;
        /// <summary>
        /// 代理端口
        /// </summary>
        private int m_proxyPort;
        /// <summary>
        /// 代理用户名
        /// </summary>
        private String m_proxyUserName;
        /// <summary>
        /// 代理密码
        /// </summary>
        private String m_proxyUserPwd;
        /// <summary>
        /// 套接字对象
        /// </summary>
        private Socket m_socket = null;
        /// <summary>
        /// 超时
        /// </summary>
        private int m_timeout;

        public byte[] m_token = new byte[4];

        /// <summary>
        /// 析构函数
        /// </summary>
        ~FCClientSocket() {
            delete();
        }

        /// <summary>
        /// 关闭服务
        /// </summary>
        /// <returns>是否关闭</returns>
        public int close() {
            int ret = -1;
            if (m_socket != null && m_socket.Connected)
            {
                try {
                    m_socket.Shutdown(SocketShutdown.Both);
                    m_socket.Close();
                    ret = 1;
                } catch (Exception ex) {
                    byte[] rmsg = new byte[1];
                    rmsg[0] = (byte)((char)2);
                    FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg, 1);
                    FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 2, "socket exit");
                    ret = -1;
                }
            }
            m_connected = false;
            return ret;
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <returns></returns>
        public ConnectStatus connect() {
            return connectStandard();
        }

        /// <summary>
        /// 临时连接状态
        /// </summary>
        private ConnectStatus m_connectState = ConnectStatus.CONNECT_SERVER_FAIL;

        /// <summary>
        /// 开始连接
        /// </summary>
        private int m_beginConnect = 0;

        /// <summary>
        /// 连接回调
        /// </summary>
        /// <param name="ar"></param>
        private void connectCallBack(IAsyncResult ar)
        {
            if (m_socket.Connected)
            {
                m_connectState = ConnectStatus.SUCCESS;
            }
            else
            {
                m_connectState = ConnectStatus.CONNECT_SERVER_FAIL;
            }
            m_beginConnect = 2;
        }

        /// <summary>
        /// 标准连接
        /// </summary>
        /// <returns>状态</returns>
        private ConnectStatus connectStandard() {
            IPAddress[] IPs = Dns.GetHostAddresses(m_ip);
            ConnectStatus status = ConnectStatus.CONNECT_SERVER_FAIL;
            IPAddress ip = IPAddress.Parse(IPs[0].ToString());
            m_socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            try {
                m_beginConnect = 1;
                m_connectState = ConnectStatus.CONNECT_SERVER_FAIL;
                //m_socket.Connect(new IPEndPoint(ip, m_port));
                m_socket.BeginConnect(ip, m_port, new AsyncCallback(connectCallBack), null);
                int tick = m_timeout * 1000;
                while (tick > 0 && m_beginConnect == 1)
                {
                    Thread.Sleep(10);
                    tick -= 10;
                }
                status = m_connectState;
                if (status != ConnectStatus.SUCCESS)
                {
                    m_connected = false;
                    m_socket.Close();
                }
                else
                {
                    m_connected = true;
                    Thread tThread = new Thread(new ThreadStart(run));
                    tThread.Start();
                    byte[] submitStr = new byte[4];
                    //submitStr[0] = (byte)'m';
                    //submitStr[1] = (byte)'i';
                    //submitStr[2] = (byte)'a';
                    //submitStr[3] = (byte)'o';
                    submitStr[0] = m_token[0];
                    submitStr[1] = m_token[1];
                    submitStr[2] = m_token[2];
                    submitStr[3] = m_token[3];
                    send(submitStr, 4);
                }
            } catch (Exception ex) {
            }
            return status;
        }

        /// <summary>
        /// 删除
        /// </summary>
        public void delete() {
            if (!m_isDeleted) {
                close();
                m_connected = false;
                m_isDeleted = true;
            }
        }

        /// <summary>
        /// 运行
        /// </summary>
        public void run() {
            byte[] str = null;
            bool get = false;
            int head = 0, pos = 0, strRemain = 0, bufferRemain = 0, index = 0, len = 0;
            int intSize = 4;
            byte[] headStr = new byte[4];
            int headSize = 4;
            while (m_connected) {
                try {
                    byte[] buffer = new byte[1024];
                    len = m_socket.Receive(buffer);
                    if (len == 0 || len == -1) {
                        byte[] rmsg = new byte[1];
                        rmsg[0] = (byte)((char)3);
                        FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg, 1);
                        FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 3, "socket error");
                        break;
                    }
                    index = 0;
                    while (index < len) {
                        int diffSize = 0;
                        if (!get) {
                            diffSize = intSize - headSize;
                            if (diffSize == 0) {
                                head = (((buffer[index] | (buffer[index + 1] << 8)) 
                                    | (buffer[index + 2] << 0x10)) | (buffer[index + 3] << 0x18));
                            } else {
                                for (int i = 0; i < diffSize; i++) {
                                    headStr[headSize + i] = buffer[i];
                                }
                                head = (((headStr[0] | (headStr[1] << 8))
                                    | (headStr[2] << 0x10)) | (headStr[3] << 0x18));
                            }
                            if (str != null) {
                                str = null;
                            }
                            str = new byte[head];
                            if (diffSize > 0) {
                                for (int i = 0; i < headSize; i++) {
                                    str[i] = headStr[i];
                                }
                                pos += headSize;
                                headSize = intSize;
                            }
                        }
                        bufferRemain = len - index;
                        strRemain = head - pos;
                        get = strRemain > bufferRemain;
                        int remain = Math.Min(strRemain, bufferRemain);
                        Array.Copy(buffer, index, str, pos, remain);
                        pos += remain;
                        index += remain;
                        if (!get) {
                            FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, str, head);
                            head = 0;
                            pos = 0;
                            if (len - index == 0 || len - index >= intSize) {
                                headSize = intSize;
                            } else {
                                headSize = bufferRemain - strRemain;
                                for (int j = 0; j < headSize; j++) {
                                    headStr[j] = buffer[index + j];
                                }
                                break;
                            }
                        }
                    }
                } catch (Exception ex) {
                    break;
                }
            }
            byte[] rmsg2 = new byte[1];
            rmsg2[0] = (byte)((char)2);
            FCClientSockets.recvClientMsg(m_hSocket, m_hSocket, rmsg2, 1);
            FCClientSockets.writeClientLog(m_hSocket, m_hSocket, 2, "socket exit");
            m_connected = false;
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="str">数据</param>
        /// <param name="len">长度</param>
        /// <returns>状态</returns>
        public int send(byte[] str, int len) {
            try
            {
                if (m_socket == null || !m_connected)
                {
                    return -1;
                }
                return m_socket.Send(str);
            }
            catch (Exception ex)
            {
                return -1;
            }
        }
    }
}
